package com.liu.tools.conf;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;

import com.liu.tools.StrUtil;

/**
 * @DateTime 2014年12月10日 上午10:11:04
 * @Company 华视传媒
 * @Author 刘兴密
 * @QQ 63972012
 * @Desc 配置加载扩展类，通过该类可自由加载配置文件
 */
public class ConfigurerEx extends PropertyPlaceholderConfigurer {

	private Resource[] locations; // 重新定义父类中的这个同名属性
	private boolean ignoreResourceNotFound = false; // 重新定义父类中的这个同名属性
	private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); // 重新定义父类中的这个同名属性
	private String fileEncoding; // 配置文件编码
	private String configPath; // 配置文件目录
	private String[] configList; // 配置文件列表
	// private Resource keyLocation; //用于指定密钥文件
	private static Map<String, String> propertiesMap;
	private Properties props;
	private ConfigurableListableBeanFactory beanFactory;

	private static final Logger logger = LoggerFactory.getLogger(ConfigurerEx.class);

	public void init() {
		this.configPath = LocalConfig.configPath;
		this.initLocations(this.configPath);
	}

	/**
	 * Set a location of a properties file to be loaded.
	 * <p>
	 * Can point to a classic properties file or to an XML file that follows JDK
	 * 1.5's properties XML format.
	 */
	public void setLocation(Resource location) {
		this.locations = new Resource[] { location };
	}

//	/**
//	 * Set locations of properties files to be loaded.
//	 * <p>
//	 * Can point to classic properties files or to XML files that follow JDK
//	 * 1.5's properties XML format.
//	 * <p>
//	 * Note: Properties defined in later files will override properties defined
//	 * earlier files, in case of overlapping keys. Hence, make sure that the
//	 * most specific files are the last ones in the given list of locations.
//	 */
//	public void setLocations(Resource[] locations) {
//		this.locations = locations;
//	}

	public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
		this.ignoreResourceNotFound = ignoreResourceNotFound;
	}

	/**
	 * @return the configList
	 */
	public String[] getConfigList() {
		return configList;
	}

	/**
	 * @param configList
	 *            the configList to set
	 */
	public void setConfigList(String[] configList) {
		this.configList = configList;
	}

	/**
	 * @return the fileEncoding
	 */
	public String getFileEncoding() {
		return fileEncoding;
	}

	/**
	 * @param fileEncoding
	 *            the fileEncoding to set
	 */
	public void setFileEncoding(String fileEncoding) {
		this.fileEncoding = fileEncoding;
	}



	/*
	 * @Override protected void processProperties(
	 * ConfigurableListableBeanFactory beanFactory, Properties props) throws
	 * BeansException { super.processProperties(beanFactory, props);
	 * 
	 * /*propertiesMap = new HashMap<String, String>(); for (Object key :
	 * props.keySet()) { String keyStr = key.toString(); propertiesMap.put(
	 * keyStr, parseStringValue(props.getProperty(keyStr), props, new
	 * HashSet())); } }
	 * 
	 * protected String resolvePlaceholder(String placeholder, Properties props)
	 * { String placeholderValue = props.getProperty(placeholder);
	 * /*if(this.secutiry){ placeholderValue = deEncrypt(placeholderValue); }
	 * return placeholderValue; }
	 */

	public Properties getProps() {
		return props;
	}

	public void setProps(Properties props) {
		this.props = props;
	}

	public ConfigurableListableBeanFactory getBeanFactory() {
		return beanFactory;
	}

	public void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	/**
	 * @DateTime 2014年12月10日 上午10:04:15
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 读取配置文件
	 * @param configPath
	 * void
	 */
	protected void initLocations(String configPath) {

		File confFile = new File(configPath);
		
		if (confFile.exists()) {
			this.locations = new FileSystemResource[configList.length];
			for (int i = 0; i < configList.length; i++) {
				String file = configList[i];
				String fp = this.configPath + file;
				Resource location = null;
				try {
					location = new FileSystemResource(fp).createRelative(file);
				} catch (Exception e) {
					logger.error("cmd=init:config | ret=FAIL | fileEncoding="
							+ this.fileEncoding + " | file=" + configPath + file
							+ " | msg=file is not existed.",e.getMessage());
					continue;
				}
				this.locations[i] = location;
			}
			logger.info("cmd=init:config | ret=OK | fileEncoding="
					+ this.fileEncoding + " | path=" + configPath
					+ " | msg=config path load ok.");
		} else {
			logger.error("cmd=init:config | ret=FAIL | fileEncoding="
					+ this.fileEncoding + " | path=" + configPath
					+ " | msg=Config Path is not exists.");
		}

	}

	/**
	 * 自定义加载配置文件方法
	 */
	@Override
	public void loadProperties(Properties props) throws IOException {

		if (this.locations != null) {
			for (Resource location : this.locations) {
				InputStream is = null;
				try {
					is = location.getInputStream();
					String filename = null;
					try {
						filename = location.getFilename();
						logger.info("cmd=init:config | ret=OK | filename=" + filename);
					} catch (IllegalStateException ex) {
						// resource is not file-based. See SPR-7552.
						logger.error("cmd=init:config ｜ret=OK | filename="	+ filename + " | msg=load file is fail", ex);
					}

					if (filename != null && filename.endsWith(".xml")) {
						this.propertiesPersister.loadFromXml(props, is);
					} else {
						if (this.fileEncoding != null) {
							this.propertiesPersister
									.load(props, new InputStreamReader(is,
											this.fileEncoding));
						} else {
							this.propertiesPersister.load(props, is);
						}
					}
				} catch (IOException ex) {
					if (this.ignoreResourceNotFound) {
						logger.error("cmd=init:config | ret=FAIL | msg=Could not load properties from "	+ location, ex);
					} else {
						throw ex;
					}
				} finally {
					if (is != null) {
						is.close();
					}
				}
			}
		}
	}

	@Override
	public void processProperties(ConfigurableListableBeanFactory beanFactory,
			Properties props) throws BeansException {
		super.processProperties(beanFactory, props);
		propertiesMap = new HashMap<String, String>();
		for (Object key : props.keySet()) {
			String keyStr = key.toString();
			String value = resolvePlaceholder(keyStr, props,SYSTEM_PROPERTIES_MODE_NEVER);
			propertiesMap.put(keyStr, value);
			// 为了安全，密码配置项不输出到日志文件
			if (isPasswordField(keyStr)) {
				logger.info(String.format("cmd=init:config | key=%s | value=****** | msg=load propertie key/value in map", keyStr, "**"));
			} else {
				logger.info(String.format("cmd=init:config | key=%s | value=%s | msg=load propertie key/value in map", keyStr, value));
			}
		}
		this.props = props;
		this.beanFactory = beanFactory;
	}

	@Override
	protected String convertProperty(String propertyName, String propertyValue) {
		//此处可对加密配置项进行处理，以后用到在写，标记下
		return super.convertProperty(propertyName, propertyValue);
	}

	@Override
	protected void convertProperties(Properties props) {
		//pwdType = Integer.parseInt(props.getProperty("db.pwd.type", "0"));
		super.convertProperties(props);
	}

	/**
	 * @DateTime 2014年12月10日 上午9:58:30
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 以String型获取配置文件中指定key的值，获取失败默认将返回　""
	 * @param name  配置文件中的key值
	 * @return
	 * String
	 */
	public static String getProperty(String name) {
		if (StrUtil.isNotNull(name)) {
			if (propertiesMap != null) {
				String val = propertiesMap.get(name);
				return (StrUtil.isNotNull(val)) ? val.trim() : "";
			}
		}
		return "";
	}


	/**
	 * @DateTime 2014年12月10日 上午9:58:04
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 以String型获取配置文件中指定key的值
	 * @param name 配置文件中的key值
	 * @param dv  获取失败的默认值
	 * @return
	 * String
	 */
	public static String getProperty(String name, String dv) {
		String val = getProperty(name);
		if (StrUtil.isNotNull(val)) {
			return val;
		} else {
			return dv;
		}
	}


	/**
	 * @DateTime 2014年12月10日 上午9:57:31
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 以int型获取配置文件中指定key的值，获取失败默认将返回0
	 * @param name  配置文件中的key值
	 * @return
	 * int
	 */
	public static int getPropertyInt(String name) {
		return getPropertyInt(name, 0);
	}


	/**
	 * @DateTime 2014年12月10日 上午9:56:41
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 以int型获取配置文件中指定key的值
	 * @param name 配置文件中的key值
	 * @param dv 获取失败的默认值
	 * @return
	 * int
	 */
	public static int getPropertyInt(String name, int dv) {
		String val = getProperty(name);
		if (StrUtil.isNotNull(val)) {
			return StrUtil.String2Int(val, dv);
		} else {
			return dv;
		}
	}
	
	/**
	 * 
	 * @param name
	 * @return "true" 或"1" 返回true，其他false
	 */
	public static boolean getPropertyBoolean(String name, boolean dv) {
	  String val = getProperty(name);
	  if (StrUtil.isNotNull(val)) {
	    val = val.trim();
	    return val.equalsIgnoreCase("true") || val.equalsIgnoreCase("1");
	  } else {
	    return dv;
	  }
	}

	/**
	 * @DateTime 2014年12月10日 上午9:57:04
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 字符串转成数组
	 * @param name
	 * @param split
	 * @return
	 * String[]
	 */
	public static String[] getPropertyArray(String name, String split) {
		String val = getProperty(name);
		split = StrUtil.isNull(split) ? "," : split;
		if (StrUtil.isNotNull(val)) {
			return val.split(split);
		} else {
			return new String[] {};
		}
	}

	/**
	 * @DateTime 2014年12月10日 上午10:07:49
	 * @Author 刘兴密
	 * @QQ 63972012
	 * @Desc 判断是否有密码项配置
	 * @param keystr
	 * @return
	 * boolean
	 */
	private static boolean isPasswordField(String keystr) {
		String key = keystr.toLowerCase();
		return (key.indexOf("superpwd") > 0 || key.indexOf("password") > 0 || key
				.indexOf("pwd") > 0);
	}
}
